package com.common.auth.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.common.api.enums.EnableStateEnum;
import com.common.api.exception.CommonException;
import com.common.api.model.PageModel;
import com.common.auth.api.dto.UserDto;
import com.common.auth.api.dto.UserPassUpdateDto;
import com.common.auth.api.service.OrganizationService;
import com.common.auth.api.service.UserRoleRelationService;
import com.common.auth.api.service.UserService;
import com.common.auth.dao.UserMapper;
import com.common.auth.dao.UserRoleMapper;
import com.common.auth.manager.PasswordManager;
import com.common.auth.po.OrganizationPo;
import com.common.auth.po.UserPo;
import com.common.auth.po.UserRolePo;
import com.common.auth.util.AuthConstant;
import com.common.util.BeanCopyUtil;
import com.common.util.GenerateSequenceUtil;
import com.common.web.dto.request.FuzzyPageDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户信息列表服务实现类
 * </p>
 *
 * @author system
 */
@Slf4j
@Service
public class UserServiceImpl extends UserService {

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private PasswordManager passwordManager;

	@Autowired
	private UserRoleMapper userRoleRelationMapper;

	@Autowired
	private UserRoleRelationService userRoleRelationService;

	@Autowired
	private OrganizationService organizationService;


	@Transactional(rollbackFor = Exception.class)
	@Override
	public int dels(List<String> idList) {
		if (CollectionUtils.isEmpty(idList)) {
			throw new CommonException("请选择要删除的用户！");
		}
		
		try {
			// 根据用户id批量查询用户信息
			List<UserPo> userPoList = validateSelectedUserByDatabase(idList);
			// 检查选中的用户中是否存在管理员用户，如果存在直接抛出异常
			checkAdminUserExistence(userPoList);
			// 使用Set存储角色编码，以去除重复项
			Set<String> userCodeSet = collectUserCodeToSet(userPoList);
			deleteUserRoleRelations(userCodeSet);
			userMapper.deleteBatchIds(idList);
			return 1;
		} catch (Exception e) {
			log.error("删除用户过程中出现异常: {}", e.getMessage());
			throw e;
		}
	}


	// 使用set收集用户编码，去除重复项
	private Set<String> collectUserCodeToSet(List<UserPo> userPos) {
		return userPos.stream().map(UserPo::getUserCode).collect(Collectors.toSet());
	}

	private List<UserPo> validateSelectedUserByDatabase(List<String> idList) {
		List<UserPo> userPoList = userMapper.selectBatchIds(idList);
		if (CollectionUtils.isEmpty(userPoList)) {
			throw new CommonException("当前要删除的用户不存在！");
		}
		return userPoList;
	}

	private void checkAdminUserExistence(List<UserPo> userPos) {
		userPos.stream()
				.filter(user -> isAdminUser(user.getUserCode()))
				.findAny()
				.ifPresent(user -> {
					String errorMsg = "管理员用户不允许被删除！";
					log.error(errorMsg);
					throw new CommonException(errorMsg);
		});
	}

	private boolean isAdminUser(String userCode) {
		return StringUtils.isNoneEmpty(userCode) && AuthConstant.ADMIN_USER.equals(userCode);
	}



	/**
	 * 删除用户角色关联表中的数据
	 * @param userCodeSet
	 */
	private void deleteUserRoleRelations(Set<String> userCodeSet) {
		if (CollectionUtils.isNotEmpty(userCodeSet)) {
			userRoleRelationMapper.delete(Wrappers.lambdaQuery(UserRolePo.class)
					.in(UserRolePo::getUserCode, userCodeSet));
		}
	}

	@Transactional(rollbackFor = Exception.class)
	public void toggleUserEnableStatus(String userCode) {
		try {
			// 参数校验
			validateUserCode(userCode);
			UserPo userPo = getUserInfo(userCode);
			// 切换角色状态 启用或者禁用
			String enableState = userPo.getEnableStateEnum();
			if(EnableStateEnum.enable.getEunmCode().equals(enableState)) {
				userPo.setEnableStateEnum(EnableStateEnum.disable.getEunmCode());
			}else {
				userPo.setEnableStateEnum(EnableStateEnum.enable.getEunmCode());
			}
			UserDto userDto = new UserDto();
			BeanCopyUtil.copy(userPo, userDto, true);
			// 检查是否为管理员账号
			checkAdminUserStatus(userDto);
			// 执行更新操作前记录日志
			log.info("即将更新用户{}的启用状态为{}", userCode, userPo.getEnableStateEnum());
			userMapper.updateById(userDto);
			// 更新成功后记录日志
			log.info("用户{}的启用状态已更新", userCode);
		} catch (Exception e) {
			log.error("启用/禁用用户出现异常, 用户编码: {}", userCode, e);
			throw e;
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public int add(UserDto dto) {
		try {
			validateAndAddUser(dto);
			return 1;
		}catch (Exception e) {
			log.error("添加用户过程中出现异常: {}", e.getMessage());
			throw e;
		}
	}

	private void validateAndAddUser(UserDto dto) {
		validateUserInput(dto);
		checkUserCodeUnique(dto.getUserCode());
		setDefaultEnableFlag(dto);
		prepareUser(dto);
		// 添加用户的时候生成默认密码
		dto.setPassword(generateUserPwd(dto.getUserCode(), null));
		userMapper.insert(dto);
	}

	private void setDefaultEnableFlag(UserDto userDto) {
		if(StringUtils.isEmpty(userDto.getEnableStateEnum())) {
			userDto.setEnableStateEnum(EnableStateEnum.enable.getEunmCode());
		}
	}

	/**
	 *  校验用户编码是否唯一
	 * @param userCode
	 */
	private void checkUserCodeUnique(String userCode) {
		if (userMapper.exists(Wrappers.lambdaQuery(UserPo.class).eq(UserPo::getUserCode, userCode))) {
			throw new CommonException("角色编码不能重复！");
		}
	}
	private void validateUserInput(UserDto dto) {
		validateDto(dto);
		validateField(dto.getUserName(), "用户名");
		validateField(dto.getUserCode(), "用户编码");
	}

	private void  validateDto(UserDto dto) {
		if (Objects.isNull(dto)) {
			throw new CommonException("用户信息不能为空！");
		}
	}

	private void  validateUserCode(String UserCode) {
		validateField(UserCode, "用户编码");
	}

	private void validateField(String value, String filedName) {
		if (StringUtils.isBlank(value)) {
			throw new CommonException(filedName + "不能为空！");
		}
	}


	/**
	 *  新增或更新用户信息
	 * @param dto
	 */
	private void prepareUser(UserDto dto) {
		if (StringUtils.isBlank(dto.getId())) {
			// 生成用户的ID
			dto.setId(GenerateSequenceUtil.generateSequenceNo());
			// 更新创建时间和更新时间
			dto.setCreateTime(new Date());
			dto.setUpdateTime(new Date());
		} else {
			dto.setUpdateTime(new Date());
		}

	}


	private String generateUserPwd(String userCode, String newPwd) {
		return  passwordManager.buildCryPassword(newPwd, userCode);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public int modify(UserDto dto) {
		try {
			validateAndModifyUser(dto);
			return 1;
		} catch (Exception e) {
			log.error("修改用户过程中出现异常: {}", e.getMessage());
			throw e;
		}
	}

	private void validateAndModifyUser(UserDto dto) {
		// 参数校验 1. 用户名和用户账号都不能为空
		validateUserInput(dto);
		// 设置用户默认启用状态
		setDefaultEnableFlag(dto);
		// 检查管理员用户状态  不允许禁用管理员用户
		checkAdminUserStatus(dto);
		// 校验要修改的用户是否存在
		UserPo userPo = checkUserExistence(dto);
		// 用户编码不允许修改
		checkUserCodeModification(dto, userPo);
		// 更新用户信息，确保数据一致性
		prepareUser(dto);
		dto.setPassword(null);
		userMapper.updateById(dto);
	}

	/**
	 *  校验当前要修改的用户是否存在
	 * @param userDto
	 * @return
	 */
	private UserPo checkUserExistence(UserDto userDto) {
		return Optional.ofNullable(userMapper.selectById(userDto.getId()))
				.orElseThrow(() -> new CommonException("当前要修改的用户不存在！"));
	}


	/**
	 * 检查管理员用户状态
	 * 	  管理员用户不允许被禁用
	 * @param dto
	 */
	private void checkAdminUserStatus(UserDto dto) {
		if (Objects.equals(AuthConstant.ADMIN_USER, dto.getUserCode()) && 
				EnableStateEnum.disable.getEunmCode().equals(dto.getEnableStateEnum())) {
			throw new CommonException("默认管理员账号不能被禁用！");
		}
	}

	/**
	 * 检查用户账号是否允许修改
	 * @param dto
	 * @param userPo
	 */
	private void checkUserCodeModification(UserDto dto, UserPo userPo) {
		checkFieldModification(userPo.getUserCode(), dto.getUserCode(), "用户账号");
	}

	private void checkFieldModification(String oldValue, String newValue, String errorMessage) {
		// 方法入参判空已经在上一步做过了
		if (!Objects.equals(oldValue, newValue)) {
			throw new CommonException(errorMessage + "不允许修改！");
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void assignRolesToUser(UserDto userDto) {
		try {
			// 参数校验 1
			validateDto(userDto);
			String userCode = userDto.getUserCode();
			// 参数校验 2
			validateUserCode(userCode);
			checkRoleCode(userCode);
			// 清空用户旧的角色关系
			clearUserRoles(userCode);
			// 新增用户角色关系
			addNewUserRoles(userCode, userDto.getUserRoleRelationPoList());

		} catch (Exception e) {
			log.error("给用户分配角色过程中出现异常: {}", e.getMessage());
			throw e;
		}
	}

	private void checkRoleCode(String userCode) {
		if(Objects.equals(AuthConstant.ADMIN_USER, userCode)) {
			throw new CommonException("不允许给管理员分配其他角色！");
		}
	}

	private void clearUserRoles(String userCode) {
		userRoleRelationService.remove(Wrappers.lambdaQuery(UserRolePo.class)
				.eq(UserRolePo::getUserCode, userCode));
	}

	private void addNewUserRoles(String userCode, List<UserRolePo> roleList) {
		if(CollectionUtils.isNotEmpty(roleList)) {
			Set<String> roleCodes = new HashSet<>();
			roleList.forEach(item -> {
				// 角色编码校验
				validateField(item.getRoleCode(), "角色编码");
				// 防止重复角色编码
				if (!roleCodes.add(item.getRoleCode())) {
					throw new CommonException("用户已拥有该角色，无需重复分配！");
				}
				item.setId(GenerateSequenceUtil.generateSequenceNo());
				item.setUserCode(userCode);
			});
			userRoleRelationService.saveBatch(roleList);
		}
	}


	/**
	 * 查询用户已经分配的角色
	 * 过滤掉已经禁用的角色
	 * @param userCode
	 * @return
	 */
	@Override
	public List<String> getAssignedRolesByUserCode(String userCode) {
		try {
			validateUserCode(userCode);
			// 通过用户编码(用户账号)userCode查询用户的角色信息
			List<UserRolePo> userRoleRelationPoList = queryUserRoleRelationByUserCode(userCode);
			return collectUserCodeByUserRoleRelation(userRoleRelationPoList);
		} catch (Exception e) {
			log.error("查询用户已分配角色过程在出现异常: {}", e.getMessage());
			throw e;
		}
	}

	/**
	 * 获取用户角色编码集合
	 * @param userRoleRelationPoList
	 * @return
	 */
	private List<String> collectUserCodeByUserRoleRelation(List<UserRolePo> userRoleRelationPoList) {
		if (CollectionUtils.isEmpty(userRoleRelationPoList)) {
			return new ArrayList<>();
		}
		//进一步获取到用户角色编码roleCode的集合
		return userRoleRelationPoList.stream()
				.map(UserRolePo::getRoleCode)
				.collect(Collectors.toList());
	}


	/**
	 * 根据用户编码查询用户关联的角色信息
	 * @param userCode
	 * @return
	 */
	private List<UserRolePo> queryUserRoleRelationByUserCode(String userCode) {
		return userRoleRelationMapper.selectList(Wrappers.lambdaQuery(UserRolePo.class)
				.eq(UserRolePo::getUserCode, userCode));
	}


	@Transactional(rollbackFor = Exception.class)
	@Override
	public void resetPassword(String userCode) {
		try {
			// 校验参数：1、判空
			validateUserCode(userCode);
			UserPo userPo = getUserInfo(userCode);
			// 为重置密码做对象准备
			prepareFoUpdate(userPo, null);
			UserDto userDto = new UserDto();
			BeanCopyUtil.copy(userPo, userDto, true);
			userMapper.updateById(userDto);
		} catch (Exception e) {
			log.error("重置用户密码过程中出现异常: {}", e.getMessage());
			throw e;
		}
	}

	private void prepareFoUpdate(UserPo userPo, String newPwd) {
		// 生成加密密码
		String newCryPass= generateUserPwd(userPo.getUserCode(), newPwd);
		userPo.setPassword(newCryPass);
		// 记录更新密码的时间
		userPo.setPwUpdateTime(new Date());
	}



	@Transactional(rollbackFor = Exception.class)
	@Override
	public void updatePassword(UserPassUpdateDto dto) {
		try {
			// 校验参数：1、判空
			validateInput(dto);
			// 校验参数：2、校验用户是否存在
			UserPo userInfo = getUserInfo(dto.getUserCode());
			// 验证密码
			validatePassword(dto.getOldPassword(), userInfo.getPassword(), userInfo.getUserCode());
			// 更新密码
			updatePassword(userInfo, dto.getNewPassword());
			UserDto userDto = new UserDto();
			BeanCopyUtil.copy(userInfo, userDto, true);
			userMapper.updateById(userDto);
		} catch (Exception e) {
			log.error("修改用户密码过程中出现异常: {}", e.getMessage());
			throw e;
		}
	}


	private void validateInput(UserPassUpdateDto dto) {
		if (Objects.isNull(dto)) {
			throw new CommonException("请输入当前要修改的密码的相关信息！");
		}
		validateField(dto.getOldPassword(), "用户原密码");
		validateField(dto.getOldPassword(), "用户新密码");
		validateField(dto.getUserCode(), "用户编码");
	}

	/**
	 *  校验当前要操作的用户是否存在
	 * @param userCode
	 * @return
	 */
	private UserPo getUserInfo(String userCode) {
		UserPo userPo = userMapper.selectOne(Wrappers.lambdaQuery(UserPo.class)
				.eq(UserPo::getUserCode, userCode));
		if (Objects.isNull(userPo)) {
			throw new CommonException("当前用户不存在！");
		}
		return userPo;
	}

	private void validatePassword(String inputPassword, String databasePassword, String userCode) {
		if (!passwordManager.validataPassword(inputPassword, databasePassword, userCode)) {
			log.error("密码验证失败: {}", userCode);
			throw new CommonException("密码或用户编码错误！");
		}
	}


	private void updatePassword(UserPo userInfo, String newPwd) {
		prepareFoUpdate(userInfo, newPwd);
	}


	@Override
	public PageModel<UserDto> queryList(FuzzyPageDTO fuzzyPageDTO) {
		PageModel<UserDto> page = super.queryList(fuzzyPageDTO);
		// 取出所有用户信息
		List<UserDto> result = page.getResult();
		if (CollectionUtils.isEmpty(result)) {
			return page;
		}

		List<String> orgCodeList = result.stream()
				.map(UserDto::getOrganizationCode).collect(Collectors.toList());
		// 避免空集合查询
		if (CollectionUtils.isEmpty(orgCodeList)) {
			return page;
		}

		// 根据组织编码查询组织名称
		List<OrganizationPo> orgList = fetchOrgList(orgCodeList);

		if (CollectionUtils.isEmpty(orgList)) {
			return page;
		}

		Map<String, String> orgMap = orgList.stream()
				.collect(Collectors.toMap(OrganizationPo::getOrganizationCode, OrganizationPo::getOrganizationName));

		// 将组织名称赋值给用户信息
		populateOrganizationNamesToUsers(result, orgMap);

		return page;
	}

	private List<OrganizationPo> fetchOrgList(List<String> orgCodeList) {
		try {
			LambdaQueryWrapper<OrganizationPo> queryWrapper = Wrappers.lambdaQuery(OrganizationPo.class);
			queryWrapper.in(OrganizationPo::getOrganizationCode, orgCodeList);
			return organizationService.list(queryWrapper);
		} catch (Exception e) {
			// 日志记录异常
			log.error("获取组织列表失败", e);
			throw e;
		}
	}

	private void populateOrganizationNamesToUsers(List<UserDto> users, Map<String, String> orgMap) {
		users.forEach(userDto -> {
			String orgName = orgMap.get(userDto.getOrganizationCode());
			if (StringUtils.isNotBlank(orgName)) {
				userDto.setOrganizationName(orgName);
			}
			// 移除密码
			userDto.setPassword(null);
		});
	}



	@Override
	public List<String> getSearchKeyList() {
		return Arrays.asList("user_code","user_name");
	}
}
